package com.xposed.xinhao.hook_newmbank;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.AttributeSet;

import com.tencent.smtt.sdk.WebView;
import com.xposed.xinhao.utils.ACache;
import com.xposed.xinhao.utils.JsonUtils;
import com.xposed.xinhao.utils.LogUtils;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * 民生银行V4.5
 * */
public class NewmbankHook {


    private static final String RECEIVER_ACTION_NMB_GET_QR_CODE = "com.xposed.hook.newmbank.ACTION_NMB_GET_QR_CODE";
    private static final String RECEIVER_ACTION_NMB_SEND_QR_CODE = "com.xposed.hook.newmbank.ACTION_NMB_SEND_QR_CODE";

    private Context mContext;

    private XC_LoadPackage.LoadPackageParam lpparam;

    //保存cookie值
    private Map<String, String> cookieMaps = new HashMap<>();

    private Object WebKitEncryForCmbcObject;
    private Object WebViewObject;
    private String AESData;
    private String callbackKeyQrCode;
    private ACache aCache;

    public NewmbankHook(XC_LoadPackage.LoadPackageParam lpparam) {
        this.lpparam = lpparam;
    }

    public void hookMethod() {

        // 勾住 activity 所有的onCreate方法
        XposedHelpers.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                mContext = (Context) param.thisObject;
                LogUtils.e("Activity onCreate：" + mContext.getClass().getName());

                if (mContext.getClass().getName().equals("cn.com.cmbc.newmbank.activity.MainActivity")) {


                    NMBBroadcastReceiver receiver = new NMBBroadcastReceiver();
                    IntentFilter intentFilter = new IntentFilter();
                    intentFilter.addAction(RECEIVER_ACTION_NMB_GET_QR_CODE);
                    (mContext).registerReceiver(receiver, intentFilter);
                    LogUtils.e("cn.com.cmbc.newmbank.activity.MainActivity 注册广播");

                    //HOOK获取cookie值
                    XposedHelpers.findAndHookMethod("com.tencent.smtt.sdk.CookieManager", mContext.getClassLoader(), "setCookie", String.class, String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            String cookie = (String) param.args[1];
                            cookieMaps.put(cookie.substring(0, cookie.indexOf("=")), cookie.substring(0, cookie.indexOf(";") + 1));
                        }
                    });

                    //HOOK WebView构造器进行初始化
                    final Class<?> cls = XposedHelpers.findClassIfExists("com.tencent.smtt.sdk.WebView", mContext.getClassLoader());
                    XposedHelpers.findAndHookConstructor(cls, Context.class, AttributeSet.class, new XC_MethodHook() {
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            WebViewObject = param.thisObject;
                        }
                    });
                    XposedHelpers.findAndHookMethod(cls, "loadUrl", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            LogUtils.e("URL地址=" + param.args[0]);
                        }
                    });

                    //HOOK WebKitEncryForCmbc构造器进行初始化
                    Class<?> WebKitEncryForCmbcClass = XposedHelpers.findClass("cn.com.cmbc.newmbank.webkitjsimpl.WebKitEncryForCmbc", mContext.getClassLoader());
                    XposedBridge.hookAllConstructors(WebKitEncryForCmbcClass, new XC_MethodHook() {
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            WebKitEncryForCmbcObject = param.thisObject;
                        }
                    });
                    //HOOK加密方法
                    XposedHelpers.findAndHookMethod(WebKitEncryForCmbcClass, "AESEncryptData001", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            AESData = (String) param.args[0];
                            LogUtils.e("加密原数据AESEncryptData001=" + param.args[0]);
                        }
                    });
                    //HOOK解密方法
                    XposedHelpers.findAndHookMethod(WebKitEncryForCmbcClass, "AESDecryptData001", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            AESData = (String) param.args[0];
                            LogUtils.e("解密原数据AESDecryptData001=" + param.args[0]);
                        }
                    });
                    //HOOK加密后或解密后的数据
                    XposedHelpers.findAndHookMethod(WebKitEncryForCmbcClass, "executeJavaScript", String.class, String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(final MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            //加密后的数据
                            if (param.args[0].toString().equals("callbackSetEncryptParamCMBCComm")) {
                                LogUtils.e("加密后数据=" + param.args[0] + "," + URLDecoder(param.args[1].toString()));
//                                if (AESData.contains("cmbc.CTCPayeeUNPSCrtOrderOp")){
//                                    //过滤出请求二维码数据加密后的数据
//                                }
                            }
                            //解密后的数据
                            if (param.args[0].toString().equals("aESProcessResult")) {
                                LogUtils.e("解密后数据=" + param.args[0] + "," + URLDecoder(param.args[1].toString()));
                                String data = URLDecoder(param.args[1].toString());
                                if (data.contains(callbackKeyQrCode)) {
                                    LogUtils.e("得到二维码信息=" + param.args[0] + "," + URLDecoder(param.args[1].toString()));
                                    Intent intent = new Intent();
                                    intent.setAction(RECEIVER_ACTION_NMB_SEND_QR_CODE);
                                    intent.putExtra("payInfo", URLDecoder(param.args[1].toString()));
                                    mContext.sendBroadcast(intent);
                                }
                            }
                        }
                    });






                    XposedHelpers.findAndHookMethod("cn.com.cmbc.newmbank.util.MBankNetUtil",mContext.getClassLoader(),"b",Context.class,String.class,String.class,String.class,new XC_MethodHook(){
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            LogUtils.e("okHttp MBankNewUtil=" +param.args[0] + ","+ param.args[1] + "," + param.args[2] + "," + param.args[3]);
                        }
                    });

                    XposedHelpers.findAndHookMethod("cn.com.cmbc.newmbank.impl.a.a", mContext.getClassLoader(), "decryptData", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            LogUtils.e("okHttp 解密数据decryptData=" + param.args[0]);
                        }

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            String jsonData = URLDecoder(param.getResult().toString());
                            if (jsonData != null){
                                JSONObject jsonObject = new JSONObject(jsonData);
                                JSONArray list = jsonObject.optJSONArray("list");
                                if (list != null && list.length() != 0) {
                                    List<String> orders = new ArrayList<>();
                                    for (int i = (list.length()-1);i>=0;i--){
                                        JSONObject object = list.getJSONObject(i);
                                        String incomeExpensesType =object.optString("incomeExpensesType");
                                        if (incomeExpensesType.equals("1")){
                                            orders.add(object.toString());
                                        }

                                        //如需要過濾使用以下代碼
                                        //String transTime = object.optString("transTime");
                                        //if (incomeExpensesType.equals("1") && Long.valueOf(transTime) > (aCache.getAsString("transTime") == null ? 0:Long.valueOf(aCache.getAsString("transTime")))){
                                        //    aCache.put("transTime",transTime);
                                        //    orders.add(object.toString());
                                        //}
                                    }
                                    //{"summary":"跨行消费:银联扫码转账","txnNum":"50224201911010939157820","balance":"0.00","transAmount":"0.10","transTime":"20191101093916","reciprocalAccount":"9502000001000001","openBankName":"8802-no defined 0005-no defined","reciprocalAccountName":"","incomeExpensesType":"0"}
                                    //{"summary":"银联入账-境内转账","txnNum":"50216201910311614140911","balance":"0.00","transAmount":"0.10","transTime":"20191031161414","reciprocalAccount":"6217003860002167482","openBankName":"","reciprocalAccountName":"古志旺","incomeExpensesType":"1"}
                                    if (orders.size() != 0){
                                        //在此發送廣播，訂單列表
                                        LogUtils.e("訂單列表：" +JsonUtils.objectToString(orders));
                                    }
                                }
                            }
                            LogUtils.e("okHttp 解密返回decryptData=" + URLDecoder(param.getResult().toString()));
                        }
                    });
                    XposedHelpers.findAndHookMethod("cn.com.cmbc.newmbank.impl.a.a", mContext.getClassLoader(), "encryptData", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            LogUtils.e("okHttp 加密数据encryptData=" + param.args[0]);
                        }

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            LogUtils.e("okHttp 加密返回encryptData=" + param.getResult());
                        }
                    });

                    aCache = ACache.get(mContext);

                    Timer timer = new Timer();
                    TimerTask task = new TimerTask() {
                        @Override
                        public void run() {
                            callMethodOrder();
                        }
                    };
                    timer.scheduleAtFixedRate(task, 5000, 5000);
                }
            }
        });

    }

    private void callMethodOrder(){
        String arg1 = "https://m1.cmbc.com.cn/CMBC_MBServer/svt/transservlet.shtml?isNewClientFour=1";
        String arg2 = "{\"pageSize\":20,\"reStartId\":1,\"account\":\"6216911505201884\",\"accountType\":\"1\",\"sonAccount\":\"0001\",\"queryBeginDate\":\"20191101\",\"queryEndDate\":\"20191101\",\"debCreInd\":\"0\",\"cardType\":\"1\",\"accountSeq\":\"0001\",\"orderFlag\":\"1\",\"mamFlag\":\"0\",\"openDate\":\"20180111\",\"TransId\":\"queryCustAcctTransDetailByAccountForListNew\"}";
        String arg3 = "002";
        Class<?> MBankNetUtil = XposedHelpers.findClassIfExists("cn.com.cmbc.newmbank.util.MBankNetUtil",mContext.getClassLoader());
        XposedHelpers.callStaticMethod(MBankNetUtil,"b",mContext,arg1,arg2,arg3);
    }

    private void getRelevantData(final String transAmt) {

        Class<String> bClass = (Class<String>) XposedHelpers.findClassIfExists("com.tencent.smtt.sdk.ValueCallback", mContext.getClassLoader());
        Object bObject = Proxy.newProxyInstance(mContext.getClassLoader(), new Class[]{bClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                LogUtils.e("获取callbackKey值：" + args[0].toString());
                callbackKeyQrCode = args[0].toString();
                SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
                String str = "{\"data\" :{\"transAccount\":\"6216911503842846\",\"transAmt\":\"" + transAmt + "\",\"urldate\":\"" + sdf.format(new Date()) + "\",\"TransId\":\"cmbc.CTCPayeeUNPSCrtOrderOp\",\"callbackKey\":" + callbackKeyQrCode + "},\"callback\":\"callbackSetEncryptParamCMBCComm\"}";
                XposedHelpers.callMethod(WebKitEncryForCmbcObject, "AESEncryptData001", str);
                return null;
            }
        });
        XposedHelpers.callMethod(WebViewObject, "evaluateJavascript", "javascript:generateRandom(20)", bObject);
    }

    private void doPostGetQrCode(String data, String cookie) {
        OkHttpClient client = new OkHttpClient();

        MediaType mediaType = MediaType.parse("text/plain");
        RequestBody body = RequestBody.create(mediaType, data);
        Request request = new Request.Builder()
                .url("https://m1.cmbc.com.cn/CMBC_MBServer/svt/transservlet.Ihtml")
                .post(body)
                .addHeader("Content-Type", "text/plain")
                .addHeader("Cookie", cookie)
                .addHeader("Accept", "*/*")
                .addHeader("Cache-Control", "no-cache")
                .addHeader("Host", "m1.cmbc.com.cn")
                .build();
        try {
            Response response = client.newCall(request).execute();
            if (response.code() != 200)
                return;
            final ResponseBody responseBody = response.body();
            if (responseBody == null)
                return;
            LogUtils.e("请求返回：" + responseBody.string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * url 解码
     */
    private static String URLDecoder(String urlCode) {
        try {
            return URLDecoder.decode(urlCode, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * url 编码
     */
    private static String URLEncoder(String urlCode) {
        try {
            return URLEncoder.encode(urlCode, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 广播监听
     */
    private class NMBBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction() == null)
                return;
            if (intent.getAction().equals(RECEIVER_ACTION_NMB_GET_QR_CODE)) {
                LogUtils.e("接受到“获取收款码”广播");
                String amount = intent.getStringExtra("amount");
                getRelevantData(amount);
            }
        }
    }
}
